package ru.spbau.krasko.phonelock.dynamic;

import java.util.Vector;

import net.rim.device.api.ui.Field;
import net.rim.device.api.ui.Graphics;
import ru.spbau.krasko.phonelock.main.LockScreen;
import ru.spbau.krasko.phonelock.utils.VectorUtils;
import ru.spbau.krasko.phonelock.utils.VectorUtils.DynamicFieldMapper;

public abstract class DynamicField extends Field {
	public final static Vector instances = new Vector();

	// running animation logic
	private final static Thread thread;
	static {
		thread = new Thread() {
			public void run() {
				while (true) {
					final boolean[] sleep = { true };
					synchronized (instances) {
						VectorUtils.forAllDynamicFields(instances,
								new VectorUtils.DynamicFieldMapper() {
									public void map(DynamicField df) {
										df.tick();
										if (Math.abs(df.desiredPos[0]
												- df.realPos[0])
												+ Math.abs(df.desiredPos[1]
														- df.realPos[1]) > 10) {
											sleep[0] = false;
										}
									}
								});
					}
					LockScreen.INSTANCE.invalidate();
					synchronized (this) {
						try {
							if (sleep[0]) {
								wait();
							} else {
								wait(20);
							}
						} catch (InterruptedException e) {
							System.out.println(e.getMessage());
						}
					}
				}
			}
		};
		thread.start();
	}

	private final static int SCALE = 10;
	private static final int FRICTION = 7;
	private static final int ACCELERATION = 2;

	private final int[] realPos = new int[2];
	private final int[] originalPos = new int[2];
	private final int[] desiredPos = new int[2];
	private final int[] oldPos = new int[2];

	private boolean hanged = false;

	public DynamicField(int x, int y) {
		synchronized (instances) {
			originalPos[0] = SCALE * x;
			originalPos[1] = SCALE * y;
			returnToOrigin(true);
			instances.addElement(this);
		}
	}

	public static void destroyAll() {
		synchronized (instances) {
			instances.removeAllElements();
		}
	}
	
	public static void paintAll(final Graphics g) {
		VectorUtils.forAllDynamicFields(instances, new VectorUtils.DynamicFieldMapper() {			
			public void map(DynamicField df) {
				df.paint(g);
			}
		});
	}
	
	private static final int DIST_FOR_HANDLING = 60;
	public static DynamicField getFieldAt(final int x, final int y, final DynamicField handling) {
		final DynamicField[] result = {null};
		VectorUtils.forAllDynamicFields(instances,
				new DynamicFieldMapper() {
					public void map(DynamicField df) {
						int dist = Math.abs(x - df.getRealX())
								+ Math.abs(y - df.getRealY());
						if (dist < DIST_FOR_HANDLING && df != handling) {
							result[0] = df;							
						}
					}
				});
		return result[0];
	}

	public abstract void paint(Graphics g);

	public void layout(int w, int h) {
	}

	public void setHanged(boolean hanged) {
		this.hanged = hanged;
	}

	public void setDesiredPos(int x, int y) {
		synchronized (thread) {
			desiredPos[0] = SCALE * x;
			desiredPos[1] = SCALE * y;
			thread.notify();
		}
	}

	public void setOriginalPos(int x, int y) {
		originalPos[0] = SCALE * x;
		originalPos[1] = SCALE * y;
	}

	public int getRealX() {
		return realPos[0] / SCALE;
	}

	public int getRealY() {
		return realPos[1] / SCALE;
	}

	public void tick() {
		int tmpX = realPos[0];
		int tmpY = realPos[1];
		if (hanged) {
			realPos[0] = desiredPos[0];
			realPos[1] = desiredPos[1];
		} else {
			realPos[0] = ((20 - FRICTION) * realPos[0] - (10 - FRICTION)
					* oldPos[0] + (desiredPos[0] - realPos[0]) * ACCELERATION) / 10;
			realPos[1] = ((20 - FRICTION) * realPos[1] - (10 - FRICTION)
					* oldPos[1] + (desiredPos[1] - realPos[1]) * ACCELERATION) / 10;
		}
		oldPos[0] = tmpX;
		oldPos[1] = tmpY;
	}

	public static void allReturnToOrigins(final boolean instantly,
			final DynamicField except) {
		synchronized (thread) {
			VectorUtils.forAllDynamicFields(instances,
					new VectorUtils.DynamicFieldMapper() {
						public void map(DynamicField df) {
							if (df != except)
								df.returnToOrigin(instantly);
						}
					});
		}
	}

	public void returnToOrigin(boolean instantly) {
		synchronized (thread) {
			if (instantly) {
				System.arraycopy(originalPos, 0, realPos, 0, 2);
				System.arraycopy(originalPos, 0, oldPos, 0, 2);
			}
			System.arraycopy(originalPos, 0, desiredPos, 0, 2);
			thread.notify();
		}
	}

	public abstract void initInteraction(DynamicField df);

	public void interact(DynamicIcon df) {
	}

	public void interact(AppChooser ac) {
	}
}
